#include "types.h"
#include "fcntl.h"
#include "user.h"
#include "roses.h"
#include "semaphore.h"
#include "boundedbuffer.h"
#include "fcntl.h"

#define STACK_SIZE 4096
typedef struct my_Request Request;
typedef struct my_Cup Cup;

int H = -1; //Num of hostesses
int B = -2; //Num of bartenders
int R = -3; //Size of RBB
int C = -4; //Size of CBB
int totalRequests = -5;//Size of total requests
int cups_added = 0;
struct BB* CBB;
struct BB* RBB;
int request_id = 0;
int cups_id = 0;
//struct semaphore* request_lock;
//struct semaphore* CBB_lock;
//struct semaphore* cups_lock;//a lock to sync the busboy and the bartender's work
int request_lock;
int RBB_lock;
int CBB_lock;
int cups_lock;
int perc15;
int perc85;
int exit_flag;
int bartenders_leave;
int bartenders_count;
int logfd;
int orders_to_complete;

void addNewRequest(Request* newReq){
	semaphore_put(RBB,newReq);
}

Request* getRequest(){
	Request* request = semaphore_pop(RBB);
	return request;
}

Cup* getCleanCup(){
	Cup* cup = (Cup*) semaphore_pop(CBB);
	return cup;
}

int washCups(){
	int cups_cleaned = 0;
	int cupsToClean = 0;
	int i;

	binary_sem_down(CBB_lock);
	cupsToClean = perc85 - CBB->index_of_last_element;//this calculation guarantees that we don't have a false wakeup
	binary_sem_up(CBB_lock);
	for(i = 0; i < cupsToClean; i++){
		Cup* cup = (Cup*) malloc(sizeof(Cup));
		semaphore_put(CBB,cup);
		cups_added++;
		cups_cleaned++;

	}
	//binary_sem_up(CBB_lock);
	return cups_cleaned;//TODO check when to return -1
}

void busboy(){
	int wash_result = -1;
	while(bartenders_leave == 0){
		binary_sem_down(cups_lock);
		if(bartenders_leave == 1){
			break;
		}
		binary_sem_down(CBB_lock);
		if(CBB->index_of_last_element < perc15){
			binary_sem_up(CBB_lock);
			wash_result = washCups();
			printf(logfd,"Busboy %d added %d clean cups\n", thread_getid(), cups_added);
			//printf(1,"Busboy %d added %d clean cups CBB is %d\n", thread_getid(), cups_added,CBB->index_of_last_element);
			cups_added = 0;
		}
		else{
			binary_sem_up(CBB_lock);
		}
	}
	//printf(1,"busboy about to exit\n");
	thread_exit(0);

}

void bartender(){
	//while((exit_flag == 0) || (RBB->index_of_last_element) > 0){
	while(orders_to_complete > 0){
		//printf(1,"got here1\n");
		binary_sem_down(RBB_lock);
		//if((RBB->index_of_last_element == 0) && (exit_flag == 1)){
		if(orders_to_complete <= 0){
			binary_sem_up(RBB_lock);
			break;
		}
		else{
		//printf(1,"got here2\n");
		orders_to_complete--;
		Request* request = getRequest();
		binary_sem_up(RBB_lock);
		Cup* cup = getCleanCup();
		printf(logfd,"Bartender %d completed request %d\n",thread_getid(), request->id);
		//printf(1,"Bartender %d completed request %d CBB is %d RBB is %d\n",thread_getid(), request->id, CBB->index_of_last_element, RBB->index_of_last_element);
		
		binary_sem_down(CBB_lock);
		if(CBB->index_of_last_element <= perc15){
			binary_sem_up(cups_lock);
			binary_sem_up(CBB_lock);
		}
		else{
			binary_sem_up(CBB_lock);
		}
		free(request);
		free(cup);
		sleep(10);
		}
	}
	bartenders_count++;
	if(bartenders_count == B){
		bartenders_leave = 1;
		//printf(1,"about to wake up busboy\n");
		binary_sem_up(cups_lock);
	}

	//printf(1,"--------------------------bartender is out---------------------------------\n");
	thread_exit(0);
}

void hostess(){
	//sem_down(request_lock);
	binary_sem_down(request_lock);
	//int i;
	while(totalRequests > 0){ //continue looping
		Request* request = malloc(sizeof(Request));
		request->id = request_id++;
		totalRequests--;
		binary_sem_up(request_lock);
		addNewRequest(request);
		printf(logfd,"Hostess %d added a new request %d\n",thread_getid(),request->id);
		//printf(1,"Hostess %d added a new request %d RBB is %d\n",thread_getid(),request->id, RBB->index_of_last_element);
		sleep(10);
		binary_sem_down(request_lock);
	}

		binary_sem_up(request_lock);
		thread_exit(0);

}

void readFromConfig() {

    	char buffer[100];
    	char value[10];

    	int ConfigFile;
	int bufferIndex = 0;
    	int valueIndex = 0;
    	int readLine = 1;

    	if ((ConfigFile = open("con.conf", O_RDONLY)) < 0){
	  	printf(1, "Failed to read from config file\n");
	  	exit();
	}

    	read(ConfigFile, buffer, sizeof(buffer));

    	while(buffer[bufferIndex] != 0){

        	//skip all non numbered chars
        	while(buffer[bufferIndex] < 48 || buffer[bufferIndex] > 57 ){
            		bufferIndex++;
        	}

		//read values into value array
        	while( 48 <= buffer[bufferIndex] && buffer[bufferIndex] <= 57 ){
            		value[valueIndex] = buffer[bufferIndex];
            		valueIndex++;
            		bufferIndex++;
        	}

		if (readLine == 1){
			B = atoi(value);
			printf(1, "B is %d\n", B);
		}
		else if (readLine == 2){
			H = atoi(value);
			printf(1, "H is %d\n", H);
		}
		else if (readLine == 3){
			R = atoi(value);
			printf(1, "R is %d\n", R);
		}
		else if (readLine == 4){
			C = atoi(value);
			printf(1, "C is %d\n", C);
		}
		else if (readLine == 5){
			totalRequests = atoi(value);
			printf(1, "totalRequests is %d\n", totalRequests);
			break;
		}

		for(valueIndex = 0; valueIndex < 10; valueIndex++){
			value[valueIndex] = 0;
		}
		valueIndex = 0;
        	readLine++;
    	}
	close(ConfigFile);
}

int main(int argc, char *argv[])
{
	//initialization semaphores + array of hostesses
	readFromConfig();
	exit_flag = 0;
	bartenders_leave = 0;
	int i;
	void* threadStack;
	int hostesses[H];
	int bartenders[B];
	request_lock = binary_sem_create();
	CBB_lock = binary_sem_create();
	RBB_lock = binary_sem_create();
	cups_lock = binary_sem_create();
	binary_sem_down(cups_lock);
	CBB = BB_create(C);
	RBB = BB_create(R);
	perc15 = 0.15 * C;
	perc85 = 0.85 * C;
	int busboy_id = 0;
	bartenders_count = 0;
	orders_to_complete = totalRequests;
	printf(1, "total requests are %d\n", orders_to_complete);

	if((logfd = open("ass2_log.txt", O_CREATE | O_RDWR)) < 0){
	 	printf(1, "cannot create log file\n");
	 	exit();
 	}
	//create cups and place them in the CBB
	for(i = 0; i < C; i++){
		Cup* cup = malloc(sizeof(Cup));
		semaphore_put(CBB,cup);
	}

//Creating the busboy thread
	threadStack = malloc(STACK_SIZE);
	busboy_id = thread_create((void *)busboy,threadStack,STACK_SIZE);

//Creating the bartenders threads
	for(i = 0; i < B; i++){
		threadStack = malloc(STACK_SIZE);
		bartenders[i] = thread_create((void *)bartender,threadStack,STACK_SIZE);

	}

//Creating the hostesses threads
	for(i = 0; i < H; i++){
		threadStack = malloc(STACK_SIZE);
		hostesses[i] = thread_create((void *)hostess,threadStack,STACK_SIZE);
	}

//Waiting for the hostesses to finish getting requests
	for(i = 0; i < H; i++){
		thread_join(hostesses[i],0);
	}

	//printf(1,"exit flag = 1\n");
	exit_flag = 1;

	for (i = 0; i < B; i++){
		thread_join(bartenders[i],0);
	}

	//printf(1,"--------------------------------bartender_leave == 1----------------------------\n");
	thread_join(busboy_id,0);
	//printf(1, "exiting\n");


	//releasing
	destroy_buffer(CBB);
	destroy_buffer(RBB);
	close(logfd);

	exit();
}
